# -*- coding: utf-8 -*-
"""
@author: dengpanxiao(@126.com)

@file: db.py

@time: 17/12/3 下午2:57

@desc:封装SQL基本操作的模块

"""
import time, uuid, functools, threading, logging


# 重定义Dict类，实现点访问key值
class Dict(dict):
    def __iter__(self, name=(), value=(), **kw):
        super(Dict, self).__init__(**kw)
        for k, v in zip(name, value):
            self[k] = v

    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError("'Dict' object has no attribute '%s'" % key)

    def __setattr__(self, key, value):
        self[key] = value


def next_id(t=None):
    '''
    使用当前时间生成长度为50的十六进制ID
    :param t: 指定时间戳
    :return: 生成的ID
    '''
    if t is None:
        t = time.time()
    return '%015d%s000' % (int(t * 1000), uuid.uuid4().hex)


def _profiling(start, sql=''):
    t = time.time() - start
    if t > 0.1:
        logging.warning('[PROFILING][DB]%s:%s' % (t, sql))
    else:
        logging.info('[PROFILING][DB]%s:%s' % (t, sql))


# 异常类
class DBError(Exception):
    pass


class MultiColumnsError(Exception):
    pass


# 数据库连接类
class _LasyConnection(object):
    def __init__(self):
        self.connection = None

    def cursor(self):
        if self.connection is None:
            connection = engine.connect()
            logging.info('open connection <%s>... ' % hex(id(connection)))
            self.connection = connection
        return self.connection.cursor()

    def commit(self):
        return self.connection.commit()

    def rollback(self):
        return self.connection.rollback()

    def cleanup(self):
        if self.connection:
            connection = self.connection
            self.connection = None
            logging.info('close connection <%s>... ' % hex(id(connection)))
            connection.close()


# 持有数据库连接的上下文对象，threading.local对象，一个线程仅有一次连接
class _DbCtx(threading.local):
    def __int__(self):
        self.connection = None
        self.transactions = 0

    def is_init(self):
        return not self.connection is None

    def init(self):
        logging.info('open lazy connection...')
        self.connection = _LasyConnection()
        self.transactions = 0

    def cleanup(self):
        self.connection.cleanup()
        self.connection = None

    def cursor(self):
        return self.connection.cursor()


# 全局的thread local数据库上下文
_db_ctx = _DbCtx()

# 全局引擎对象
engine = None


# 数据库引擎对象
class _Engine(object):
    def __init__(self, connect):
        self._connect = connect

    def connect(self):
        return self._connect


# 初始化，创建数据库连接信息
def create_engine(user, password, database, host, port, **kw):
    import mysql.connector
    global engine
    if engine is not None:
        raise DBError('Engine is already initialized.')
    params = dict(user=user, password=password, database=database, host=host, port=port)
    defaults = dict(use_unicode=True, charset='utf8', collation='utf8_general_ci', autocommit=False)
    for k,v in defaults.iteritems():
        params[k] = kw.pop(k, v)
    params.update(kw)
    params['buffered'] = True
    engine = _Engine(lambda :mysql.connector.connect(params))
    logging.info('Init mysql engine <%s> ok' % hex(id(engine)))


# 数据库连接上下文，自动获取和释放连接
class _ConnectionCtx(object):
    def __enter__(self):
        global _db_ctx
        self.should_cleanup = False
        if not _db_ctx.is_init():
            _db_ctx.init()
            self.should_cleanup = True
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        global _db_ctx
        if self.should_cleanup:
            _db_ctx.cleanup()


# 连接数据库
def connection():
    return _ConnectionCtx()


# 装饰connection，使其可以使用with方式复用1个连接进行多次操作
def with_connection(func):
    @functools.warps(func)
    def _warpper(*args, **kw):
        with _ConnectionCtx():
            return func(*args, **kw)
    return _warpper


# 数据库事务
class _Transaction(object):
    def __enter__(self):
        global _db_ctx
        self.should_close_conn = False
        if not _db_ctx.is_init():
            _db_ctx.init()
            self.should_close_conn = True
        _db_ctx.transactions += 1
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        global _db_ctx
        _db_ctx.transactions -= 1
        try:
            if _db_ctx.transactions == 0:
                if exc_type is None:
                    self.commit()
                else:
                    self.rollback()
        finally:
            if self.should_close_conn:
                _db_ctx.cleanup()

    def commit(self):
        global _db_ctx
        try:
            _db_ctx.connection.commit()
        except:
            _db_ctx.connection.rollback()
            raise

    def rollback(self):
        global _db_ctx
        _db_ctx.connection.rollback()


# 查询方法
def _select(sql, first, *args):
    global _db_ctx
    cursor = None
    sql = sql.replace('?', '%s')
    logging.info('SQL: %s, ARGS: %s' % (sql, args))
    try:
        cursor = _db_ctx.connection.cursor()
        cursor.execute(sql, args)
        if cursor.description:
            names = [x[0] for x in cursor.description]
        if first:
            values = cursor.fetchone()
            if not values:
                return None
            else:
                return Dict(names, values)
        return [Dict(names, x)  for x in cursor.fetchall]
    finally:
        if cursor:
            cursor.close()


@with_connection
def select_one(sql, *args):
    return _select(sql, True, *args)


@with_connection
def select_int(sql, *args):
    d = _select(sql, True, *args)
    if len(d) != 1:
        raise MultiColumnsError('Expect only one column.')
    return d.values()[0]


@with_connection
def select(sql, *args):
    return _select(sql, False, *args)









# 数据库事务
def transaction():
    pass


# 查询方法
# @with_connection
def select(sql):
    '''
    :param sql: SQL查询语句
    :return: list，查询结果，每行用dict表示
    '''
    pass


# 增删改方法
def update(sql, *args):
    '''
    :param sql: SQL查询语句
    :param args: 替换参数
    :return: 受影响的行数
    '''
    pass


if __name__ == '__main__':
    print next_id()